﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Diagnostics;

using InfFront.UICore;

namespace InfFront.InfoViews.Base
{
    /// <summary>
    /// View-side IInfoField wrapper
    /// </summary>
    public class InfoViewField : BaseInfoViewEntity
    {
        #region Private Variables
        private IInfoField field;
        private InfoViewSource owner;
        private InfoViewFieldGroup editGroup;
        private bool isEnum;
        private List<InfoFieldEnumItem> enumItems;
        private InfoViewField referencedField;
        private InfoViewField referencedDisplayField;
        private bool referencedFieldsCached = false;
        private Dictionary<object, InfoFieldEnumItem> enumItemsByValue;
        private Dictionary<string, InfoFieldEnumItem> enumItemsByTitle;
        #endregion

        internal InfoViewField(InfoViewSource owner, IInfoField field)
            : base(field.GetName(), owner)
        {
            this.field = field;
            this.owner = owner;
            this.isEnum = field.GetIsEnum();
        }

        public InfoViewSource Owner
        {
            get { return owner; }
        }

        public string Title
        {
            get { return field.GetTitle(); }
        }

        public int Order
        {
            get 
            {
                var order = (int?)VisualSettings.GetValue(BaseSettingsManager.COLUMN_ORDER_KEY);
                if (order.HasValue && order.Value >= 0)
                    return order.Value;
                return field.GetOrder(); 
            }
            set
            {
                VisualSettings.SetValue(BaseSettingsManager.COLUMN_ORDER_KEY, value);
                FirePropertyChanged(BaseSettingsManager.COLUMN_ORDER_KEY);
            }
        }

        public int EditGroupOrder
        {
            get { return field.GetEditGroupOrder(); }
        }

        public IDataType Type
        {
            get { return field.GetType(); }
        }

        public bool IsReadOnly
        {
            get { return field.GetIsReadOnly(); }
        }

        public bool IsGridVisible
        {
            get { return field.GetIsGridVisible(); }
        }

        public bool IsEnum
        {
            get { return isEnum; }
        }

        public InfoViewFieldGroup EditGroup
        {
            get { return editGroup; }
        }

        internal void SetEditGroup(InfoViewFieldGroup group)
        {
            editGroup = group;
        }

        public InfoViewField ReferencedField
        {
            get 
            {
                EnusureReferencedFields();
                return referencedField; 
            }
        }

        public InfoViewField ReferencedDisplayField
        {
            get 
            {
                EnusureReferencedFields();
                return referencedDisplayField; 
            }
        }

        private void EnusureReferencedFields()
        {
            if (referencedFieldsCached)
                return;
            referencedFieldsCached = true;
            var infoSource = Owner.FindInfoSourceByName(field.GetReferencedInfoSourceName());
            if (infoSource == null)
                return;
            var fieldName = field.GetReferencedFieldName();
            if (!string.IsNullOrEmpty(fieldName))
                referencedField = infoSource.FieldByName(fieldName);
            fieldName = field.GetReferencedDisplayFieldName();
            if (!string.IsNullOrEmpty(fieldName))
                referencedDisplayField = infoSource.FieldByName(fieldName);
        }

        public IEnumerable<InfoFieldEnumItem> GetEnumItems()
        {
            if (enumItems == null)
            {
                enumItems = new List<InfoFieldEnumItem>();
                enumItemsByValue = new Dictionary<object, InfoFieldEnumItem>();
                enumItemsByTitle = new Dictionary<string, InfoFieldEnumItem>();
                var originalList = field.GetEnumValues();
                if (originalList != null)
                {
                    foreach (var itm in originalList)
                    {
                        var eItem = new InfoFieldEnumItem() { Title = itm.GetTitle(), Value = itm.GetValue() };
                        enumItems.Add(eItem);
                        try
                        {
                            enumItemsByValue.Add(eItem.Value, eItem);
                            enumItemsByTitle.Add(eItem.Title, eItem);
                        }
                        catch (ArgumentException)
                        {
                            Debug.Assert(false);
                            throw;
                        }
                    }
                }
            }
            return enumItems;
        }

        public Type EffectiveType
        {
            get { return field.GetType().GetTraits().Type; }
        }

        public Type EffectiveDisplayType
        {
            get 
            {
                var dispField = this.ReferencedDisplayField;
                if (dispField != null)
                    return dispField.EffectiveDisplayType;
                return EffectiveType; 
            }
        }

        public InfoFieldEnumItem GetEnumItemByValue(object value)
        {
            Debug.Assert(IsEnum);
            if (enumItemsByValue == null)
                GetEnumItems();
            InfoFieldEnumItem result = null;
            if (!enumItemsByValue.TryGetValue(value, out result))
                throw new ArgumentException(string.Format("The value '{0}' is not in the enum", value));
            return result;
        }

        public InfoFieldEnumItem GetEnumItemByTitle(string title)
        {
            Debug.Assert(IsEnum);
            if (enumItemsByTitle == null)
                GetEnumItems();
            InfoFieldEnumItem result = null;
            if (!enumItemsByTitle.TryGetValue(title, out result))
                throw new ArgumentException(string.Format("Unknown enum item caption: {0}", title));
            return result;
        }
    }

    public class InfoViewFieldGroup : BaseInfoViewEntity
    {
        #region Private Variables
        private IInfoFieldGroup group;
        private InfoViewSource owner;
        private List<InfoViewField> fields;
        #endregion

        internal InfoViewFieldGroup(InfoViewSource owner, IInfoFieldGroup group)
            : base(group.GetName(), owner)
        {
            this.group = group;
            this.owner = owner;
            this.fields = new List<InfoViewField>();
        }

        internal InfoViewSource Owner
        {
            get { return owner; }
        }

        public string Title
        {
            get { return group.GetTitle(); }
        }

        public int Order
        {
            get { return group.GetOrder(); }
        }

        public IEnumerable<InfoViewField> Fields
        {
            get { return fields; }
        }

        internal void AddField(InfoViewField field)
        {
            int order = field.EditGroupOrder;
            int i = 0;
            for (; i < fields.Count; ++i)
            {
                if (fields[i].EditGroupOrder > order)
                    break;
            }
            fields.Insert(i, field);
        }
    }
}
